package ch.ethz.fcl.metrobuzz.render.od.flow_view.sankey;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ch.ethz.fcl.metrobuzz.data.scene.MBScene;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Node;
import ch.ethz.fcl.metrobuzz.render.techniques.colors.SGMRTColorTable;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Rectangle;

public class SankeyNodeGeometry {
	private ODSankeyDiagramData data;
	private SankeyNodeGroup sn;
	private Rectangle bound;

	private Map<Node, Point> normalizedMap;

	private SankeyNodeGeometry geo;

	float norMinX, norMinY, norMaxX, norMaxY, norWidth, norHeight;
	float minX, minY, maxX, maxY, width, height;

	private List<Node> lineNodes;

	public SankeyNodeGeometry(ODSankeyDiagramData data, SankeyNodeGroup sn,
			Rectangle bound) {
		this.data = data;
		this.sn = sn;
		this.bound = bound;

		MBScene scene = data.getSLQ().getData().getScene();
		if (sn.getId().equals("EW")) {
			lineNodes = scene.getEWNodes();
			lineNodes.addAll(scene.getCGNodes());
		} else if (sn.getId().equals("NS"))
			lineNodes = scene.getNSNodes();
		else if (sn.getId().equals("NE"))
			lineNodes = scene.getNENodes();
		else if (sn.getId().equals("CC"))
			lineNodes = scene.getCCNodes();

		normalize();
	}

	private void normalize() {
		if (sn == null || bound == null || lineNodes == null)
			return;

		minX = Float.MAX_VALUE;
		minY = Float.MAX_VALUE;
		maxX = -Float.MAX_VALUE;
		maxY = -Float.MAX_VALUE;

		for (Node nnode : lineNodes) {
			minX = Math.min(minX, nnode.getX());
			maxX = Math.max(maxX, nnode.getX());
			minY = Math.min(minY, nnode.getY());
			maxY = Math.max(maxY, nnode.getY());
		}

		width = maxX - minX;
		height = maxY - minY;

		norMinX = bound.getMinX() + 6;
		norMaxX = bound.getMaxX() - 6;
		norWidth = norMaxX - norMinX;
		norMinY = bound.getMinY() + 6;
		norMaxY = bound.getMaxY() - 6;
		norHeight = norMaxY - norMinY;

		if (width > height) {
			norMinY = bound.getMinY() + bound.getHeight() / 2 - norWidth
					* height / width / 2;
			norMaxY = bound.getMinY() + bound.getHeight() / 2 + norWidth
					* height / width / 2;
		} else if (height > width) {
			norMinX = bound.getMinX() + bound.getWidth() / 2 - norHeight
					* width / height / 2;
			norMaxX = bound.getMinX() + bound.getWidth() / 2 + norHeight
					* width / height / 2;
		} else if (height == 0 && width == 0) {
			norMinX = norMaxX = bound.getMinX() + bound.getWidth() / 2;

			norMinY = norMaxY = bound.getMinY() + bound.getWidth() / 2;
		}
		norWidth = norMaxX - norMinX;
		norHeight = norMaxY - norMinY;

		normalizedMap = new HashMap<Node, Point>();
		for (Node nnode : lineNodes) {
			float norX = (nnode.getX() - minX) / width * norWidth + norMinX;
			float norY = -(nnode.getY() - minY) / height * norHeight + norMaxY;

			normalizedMap.put(nnode, new Point((int) norX, (int) norY));
		}
	}

	public void render(Graphics2D g) {
		if (sn.getId().equals("BP"))
			return;

		setColor(g, sn, 255);

		renderConnection(g);
		renderBound(g);
		rendGeometry(g);
	}

	private void setColor(Graphics2D g, SankeyNode sng, int alpha) {
		Color c = null;
		if (sng == null)
			c = Color.BLACK;

		else if (sng.getId().equals("EW"))
			c = SGMRTColorTable.EW_COLOR;
		else if (sng.getId().equals("NS"))
			c = SGMRTColorTable.NS_COLOR;
		else if (sng.getId().equals("CC"))
			c = SGMRTColorTable.CC_COLOR;
		else if (sng.getId().equals("NE"))
			c = SGMRTColorTable.NE_COLOR;
		else if (sng.origin)
			c = Color.RED;
		else
			c = Color.BLUE;

		g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha));
	}

	private void rendGeometry(Graphics2D g) {
		if (lineNodes == null)
			return;

		int max = lineNodes.size() - 1;

		if (sn.origin)
			g.setColor(Color.RED);
		else
			g.setColor(Color.BLUE);

		g.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		for (int i = 0; i < max; i++) {
			Node n1 = lineNodes.get(i);
			Point p1 = normalizedMap.get(n1);

			Node n2 = lineNodes.get(i + 1);
			Point p2 = normalizedMap.get(n2);

			if (n1.getId().contains("EW29") && n2.getId().contains("CG0"))
				continue;

			if (n1.getId().contains("CG") && n2.getId().contains("CG")) {
				continue;
			}

			g.drawLine(p1.x, p1.y, p2.x, p2.y);
		}

		if (sn.getId().equals("EW")) {
			MBScene scene = data.getSLQ().getData().getScene();
			Node n1 = scene.getCGNodes().get(0);
			Point p1 = normalizedMap.get(n1);

			Node n2 = scene.getCGNodes().get(1);
			Point p2 = normalizedMap.get(n2);

			g.drawLine(p1.x, p1.y, p2.x, p2.y);

			Node n3 = scene.getCGNodes().get(2);
			Point p3 = normalizedMap.get(n3);
			g.drawLine(p3.x, p3.y, p2.x, p2.y);
		}

		setColor(g, sn, 255);
		g.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		MBScene scene = data.getSLQ().getData().getScene();
		for (SankeyNode subnode : sn.getNodeList()) {
			Node n = scene.getNode(subnode.getId());
			if (n.getId().startsWith("BP"))
				continue;
			Point p = normalizedMap.get(n);
			if (p == null)
				continue;

			g.drawLine(p.x, p.y, p.x, p.y);
		}

		g.setStroke(new BasicStroke(10, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		for (SankeyNode subnode : sn.getNodeList()) {
			Node n = scene.getNode(subnode.getId());
			if (n.getId().equals(data.getSLQ().getStart().getId())) {
				g.setColor(Color.RED);
				Point p = normalizedMap.get(n);
				g.drawLine(p.x, p.y, p.x, p.y);
			} else if (n.getId().equals(data.getSLQ().getEnd().getId())) {
				g.setColor(Color.BLUE);
				Point p = normalizedMap.get(n);
				g.drawLine(p.x, p.y, p.x, p.y);
			}
		}
	}

	private void renderConnection(Graphics2D g) {
		if (sn == null)
			return;

		Rectangle snBound = sn.getBound();
		int snx = (int) snBound.getMinX() - 20, sny = (int) (snBound.getMinY() + snBound
				.getHeight() / 2);
		if (!sn.origin)
			snx = (int) snBound.getMaxX() + 20;

		int boundx = (int) bound.getMinX(), boundy = (int) (bound.getMinY() + bound
				.getHeight() / 2);
		if (sn.origin)
			boundx = (int) bound.getMaxX();

		g.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		g.drawLine(snx, sny, boundx, boundy);
	}

	private void renderBound(Graphics2D g2d) {
		int minX = (int) bound.getMinX(), minY = (int) bound.getMinY();
		int width = (int) bound.getWidth(), height = (int) bound.getHeight();

		setColor(g2d, sn, 30);
		// g2d.setColor(Color.WHITE);
		g2d.fillRect(minX, minY, width, height);

		setColor(g2d, sn, 200);
		g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		g2d.drawRect(minX, minY, width, height);
	}

	public Rectangle getBound() {
		return bound;
	}

	public SankeyNodeGeometry getGeometry() {
		return geo;
	}
}
